import time
import time
from flask import Flask, render_template, request, jsonify, send_file, send_from_directory
from flask_cors import CORS
import requests
import os
import uuid
import cv2
import numpy as np
from werkzeug.utils import secure_filename
import shutil
from datetime import datetime, timedelta
import tempfile
import subprocess
import logging

app = Flask(__name__, static_folder='static', static_url_path='/static')
CORS(app)

# 配置日志
logging.basicConfig(level=logging.INFO)
app.logger.setLevel(logging.INFO)
# ffmpeg位置
# 在代码开头添加
FFMPEG_PATH = r'C:\Program Files\ffmpeg-7.0.2-essentials_build\bin\ffmpeg.exe'
# 音频合成的目录
DOWNLOAD_FOLDER = r'c:\Users\Administrator\Desktop\2dshuziren\heygem_data\face2face\temp'

# 模特训练的目录
UPLOAD_FOLDER = r'C:\Users\Administrator\Desktop\2dshuziren\heygem_data\voice\data'

# 确保目录存在
os.makedirs(DOWNLOAD_FOLDER, exist_ok=True)
os.makedirs(UPLOAD_FOLDER, exist_ok=True)


# 音频上传API
@app.route('/api/uploads', methods=['POST'])
def upload_audio():
    if 'file' not in request.files:
        return jsonify({"error": "未提供文件"}), 400

    file = request.files['file']

    if file.filename == '':
        return jsonify({"error": "文件名为空"}), 400

    if not file.filename.endswith('.wav'):
        return jsonify({"error": "只支持 .wav 文件"}), 400

    save_path = os.path.join(UPLOAD_FOLDER, file.filename)
    file.save(save_path)

    return jsonify({
        "msg": "上传成功",
        "file": save_path.replace("\\", "/"),
        "file_path": file.filename
    })


# 视频上传API
@app.route('/api/uploads2', methods=['POST'])
def upload_audio2():
    if 'file' not in request.files:
        return jsonify({"error": "未提供文件"}), 400

    file = request.files['file']

    if file.filename == '':
        return jsonify({"error": "文件名为空"}), 400

    save_path = os.path.join(DOWNLOAD_FOLDER, file.filename)
    file.save(save_path)

    return jsonify({
        "msg": "上传成功",
        "file": save_path.replace("\\", "/"),
        "file_path": file.filename
    })


# 首页
@app.route("/")
def index():
    return render_template("index.html")


# 模特训练页面
@app.route("/index")
def index1():
    return render_template("index2.html")


# 文件列表（调试用）
@app.route('/files')
def list_files():
    files = os.listdir(DOWNLOAD_FOLDER)
    return '<br>'.join([f'<a href="/download/{f}">{f}</a>' for f in files])


# 文件下载
@app.route('/download/<path:filename>')
def download_file(filename):
    return send_from_directory(DOWNLOAD_FOLDER, filename)


# 静态文件服务
@app.route('/static/<path:filename>')
def serve_static_from_download_folder(filename):
    return send_from_directory(app.static_folder, filename)


# 代理提交API
@app.route("/api/submit", methods=["POST"])
def submit_proxy():
    data = request.get_json()
    try:
        res = requests.post("http://127.0.0.1:8383/easy/submit", json=data)
        return jsonify(res.json()), res.status_code
    except Exception as e:
        return jsonify({"error": str(e)}), 500


# 代理查询API
@app.route("/api/query", methods=["GET"])
def query_proxy():
    code = request.args.get("code")
    try:
        res = requests.get(f"http://localhost:8383/easy/query", params={"code": code})
        return jsonify(res.json()), res.status_code
    except Exception as e:
        return jsonify({"error": str(e)}), 500


# 静态文件服务
@app.route('/static/<path:filename>')
def serve_static(filename):
    return send_from_directory(app.static_folder, filename)


# 查询进度API
@app.route('/api/query_progress_sync', methods=['GET'])
def query_progress_sync_api():
    task_code = request.args.get('code')
    if not task_code:
        return jsonify({"error": "missing code"}), 400
    # 这里需要实现实际的查询逻辑
    return jsonify({"status": "not implemented"})


# 测试页面
@app.route("/index2")
def index2():
    return render_template("index-test.html")


# 静态文件列表
@app.route('/list_static')
def list_static():
    files = os.listdir(app.static_folder)
    return jsonify(files)


# 视频合成函数 - 已修复
def overlay_videos_on_background(background_path, video1_path, video2_path, output_path,
                                 pos1, size1, pos2, size2, video_scale=1.0):
    """将两个视频叠加到背景图片上，并添加第二个视频的音频"""
    try:
        app.logger.info(f"开始视频合成: {video1_path} 和 {video2_path}")

        # 读取背景图片
        background = cv2.imread(background_path)
        if background is None:
            raise ValueError(f"无法读取背景图片: {background_path}")

        # 获取背景尺寸
        bg_height, bg_width = background.shape[:2]
        app.logger.info(f"背景尺寸: {bg_width}x{bg_height}")

        # 打开视频文件
        cap1 = cv2.VideoCapture(video1_path)
        cap2 = cv2.VideoCapture(video2_path)

        if not cap1.isOpened():
            raise ValueError(f"无法打开视频文件: {video1_path}")
        if not cap2.isOpened():
            raise ValueError(f"无法打开视频文件: {video2_path}")

        # 获取视频信息
        fps = cap1.get(cv2.CAP_PROP_FPS)
        if fps <= 0:
            fps = 30.0  # 默认帧率
            app.logger.warning(f"使用默认帧率: {fps} FPS")
        else:
            app.logger.info(f"视频帧率: {fps} FPS")

        # 创建临时无声视频文件
        temp_video_path = os.path.join(tempfile.gettempdir(), f"temp_{uuid.uuid4().hex}.mp4")

        # 创建视频写入器 - 使用正确的参数格式
        fourcc = cv2.VideoWriter_fourcc(*'mp4v')
        out = cv2.VideoWriter(
            filename=temp_video_path,
            fourcc=fourcc,
            fps=fps,
            frameSize=(int(bg_width), int(bg_height)),  # 确保是整数
            isColor=True
        )

        if not out.isOpened():
            # 尝试其他编码器
            codecs = ['XVID', 'MJPG', 'H264', 'avc1']
            for codec in codecs:
                try:
                    app.logger.info(f"尝试编码器: {codec}")
                    fourcc = cv2.VideoWriter_fourcc(*codec)
                    out = cv2.VideoWriter(
                        temp_video_path,
                        fourcc,
                        fps,
                        (int(bg_width), int(bg_height)),
                        True
                    )
                    if out.isOpened():
                        app.logger.info(f"成功使用 {codec} 编码器创建视频写入器")
                        break
                    else:
                        out.release()
                except Exception as e:
                    app.logger.warning(f"尝试 {codec} 编码器失败: {str(e)}")
            else:
                raise RuntimeError("无法创建视频写入器，尝试的编码器均失败")

        frame_count = 0
        start_time = time.time()

        try:
            while True:
                # 检查是否超时（10分钟）
                elapsed_time = time.time() - start_time
                if elapsed_time > 600:  # 10分钟 = 600秒
                    app.logger.error(f"视频处理超时 ({elapsed_time:.1f}秒 > 600秒)")
                    raise TimeoutError("视频处理超时")

                # 读取视频帧
                ret1, frame1 = cap1.read()
                ret2, frame2 = cap2.read()

                # 如果两个视频都结束则退出
                if not ret1 and not ret2:
                    break

                # 创建一个背景的副本
                bg_frame = background.copy()

                # 叠加第一个视频帧
                if ret1:
                    # 调整视频帧尺寸
                    frame1 = cv2.resize(frame1, size1)
                    # 确保视频尺寸不超过背景边界
                    x1, y1 = pos1
                    w1, h1 = size1
                    if x1 < 0: x1 = 0
                    if y1 < 0: y1 = 0
                    if x1 + w1 > bg_width: w1 = bg_width - x1
                    if y1 + h1 > bg_height: h1 = bg_height - y1

                    # 创建ROI区域并叠加
                    if w1 > 0 and h1 > 0:
                        roi = bg_frame[y1:y1 + h1, x1:x1 + w1]
                        frame1 = cv2.resize(frame1, (w1, h1))
                        roi[:] = frame1

                # 叠加第二个视频帧
                if ret2:
                    # 调整视频帧尺寸
                    frame2 = cv2.resize(frame2, size2)
                    # 确保视频尺寸不超过背景边界
                    x2, y2 = pos2
                    w2, h2 = size2
                    if x2 < 0: x2 = 0
                    if y2 < 0: y2 = 0
                    if x2 + w2 > bg_width: w2 = bg_width - x2
                    if y2 + h2 > bg_height: h2 = bg_height - y2

                    # 创建ROI区域并叠加
                    if w2 > 0 and h2 > 0:
                        roi = bg_frame[y2:y2 + h2, x2:x2 + w2]
                        frame2 = cv2.resize(frame2, (w2, h2))
                        roi[:] = frame2

                # 写入输出视频
                out.write(bg_frame)
                frame_count += 1

                # 每100帧记录一次进度
                if frame_count % 100 == 0:
                    elapsed = time.time() - start_time
                    app.logger.info(f"已处理 {frame_count} 帧, 耗时: {elapsed:.2f}秒")

        finally:
            # 释放资源
            cap1.release()
            cap2.release()
            out.release()
            cv2.destroyAllWindows()

            elapsed = time.time() - start_time
            app.logger.info(f"无声视频合成完成! 总帧数: {frame_count}, 总耗时: {elapsed:.2f}秒")

        # 添加第二个视频的音频到合成视频
        app.logger.info("开始添加第二个视频的音频到合成视频...")

        # 使用FFmpeg添加音频
        ffmpeg_cmd = [
            FFMPEG_PATH,
            '-y',  # 覆盖输出文件
            '-i', temp_video_path,  # 无声视频
            '-i', video2_path,  # 第二个视频（包含音频）
            '-c:v', 'copy',  # 复制视频流
            '-c:a', 'aac',  # 编码音频为AAC
            '-map', '0:v:0',  # 使用第一个输入的视频流
            '-map', '1:a:0',  # 使用第二个输入的音频流
            '-shortest',  # 以最短的输入流结束
            output_path  # 输出文件
        ]

        app.logger.info(f"执行FFmpeg命令: {' '.join(ffmpeg_cmd)}")

        try:
            result = subprocess.run(
                ffmpeg_cmd,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=True,
                check=True
            )
            app.logger.info(f"FFmpeg输出: {result.stdout}")
            if result.stderr:
                app.logger.warning(f"FFmpeg警告: {result.stderr}")
        except subprocess.CalledProcessError as e:
            app.logger.error(f"FFmpeg执行失败: {e.stderr}")
            raise RuntimeError(f"添加音频失败: {e.stderr}")

        # 删除临时文件
        try:
            os.remove(temp_video_path)
            app.logger.info(f"已删除临时文件: {temp_video_path}")
        except Exception as e:
            app.logger.warning(f"删除临时文件失败: {str(e)}")

        app.logger.info(f"视频合成完成: {output_path}")

    except Exception as e:
        app.logger.error(f"视频处理失败: {str(e)}", exc_info=True)
        raise


# 合成视频下载
@app.route('/download_composite/<filename>')
def serve_composite_video(filename):
    """提供合成视频下载"""
    try:
        safe_filename = secure_filename(filename)
        file_path = os.path.join(DOWNLOAD_FOLDER, safe_filename)

        # 验证文件存在
        if not os.path.exists(file_path):
            return "File not found", 404

        return send_file(file_path, as_attachment=False)

    except Exception as e:
        return jsonify({
            "error": f"下载失败: {str(e)}"
        }), 500


# PPT视频上传
@app.route('/ppt_video_upload', methods=['POST'])
def handle_ppt_video_upload():
    """处理PPT视频上传"""
    if 'file' not in request.files:
        return jsonify({"error": "未提供文件"}), 400

    file = request.files['file']

    if file.filename == '':
        return jsonify({"error": "文件名为空"}), 400

    # 文件扩展名验证
    allowed_extensions = {'.mp4', '.avi', '.mov'}
    filename = secure_filename(file.filename)
    file_ext = os.path.splitext(filename)[1].lower()

    if file_ext not in allowed_extensions:
        return jsonify({
            "error": "不支持的文件类型",
            "allowed_types": list(allowed_extensions)
        }), 400

    # 保存文件
    save_path = os.path.join(DOWNLOAD_FOLDER, filename)
    file.save(save_path)

    # 文件信息
    file_info = {
        "filename": filename,
        "file_path": filename,
        "file_size": os.path.getsize(save_path),
        "file_type": file_ext
    }

    return jsonify(file_info)


# 视频合成API - 已优化
@app.route('/api/generate_composite_video', methods=['POST'])
def create_composite_video():
    """生成合成视频"""
    try:
        app.logger.info("收到合成视频请求")
        data = request.json
        app.logger.debug(f"请求数据: {data}")

        if not data:
            return jsonify({"error": "无效的JSON数据"}), 400

        # 必填参数
        required_params = ['video1', 'video2', 'pos1', 'size1', 'pos2', 'size2']
        missing = [p for p in required_params if p not in data]
        if missing:
            error_msg = f"缺少必要参数: {', '.join(missing)}"
            app.logger.error(error_msg)
            return jsonify({"error": error_msg}), 400

        # 强制使用默认背景
        default_background = os.path.join(app.static_folder, 'backgrounds', 'default.png')
        app.logger.debug(f"使用默认背景: {default_background}")

        if not os.path.exists(default_background):
            error_msg = f"默认背景图片不存在: {default_background}"
            app.logger.error(error_msg)
            return jsonify({
                "error": error_msg,
                "suggestion": "请确保在/static/backgrounds/default.png放置背景图片"
            }), 400

        # 验证视频文件是否存在
        for file_type in ['video1', 'video2']:
            filename = data[file_type]
            file_path = os.path.join(DOWNLOAD_FOLDER, filename)
            app.logger.debug(f"检查文件: {file_type}={file_path}")

            if not os.path.exists(file_path):
                error_msg = f"视频文件不存在: {file_type}={file_path}"
                app.logger.error(error_msg)
                return jsonify({
                    "error": error_msg,
                    "suggestion": "请确保视频文件已成功上传"
                }), 400

        # 创建输出文件
        output_filename = f"composite_{uuid.uuid4().hex}.mp4"
        output_path = os.path.join(DOWNLOAD_FOLDER, output_filename)
        app.logger.info(f"输出文件路径: {output_path}")

        # 转换坐标和尺寸参数
        def parse_coords(param_name, expected_length=2):
            coords = data.get(param_name, [])
            if len(coords) != expected_length:
                return None
            try:
                return tuple(int(x) for x in coords)
            except (TypeError, ValueError):
                return None

        pos1 = parse_coords('pos1')
        size1 = parse_coords('size1')
        pos2 = parse_coords('pos2')
        size2 = parse_coords('size2')

        if None in [pos1, size1, pos2, size2]:
            error_msg = "坐标和尺寸参数必须是包含两个整数的数组"
            app.logger.error(error_msg)
            return jsonify({"error": error_msg}), 400

        # 调用视频处理函数
        app.logger.info("开始合成视频...")
        overlay_videos_on_background(
            background_path=default_background,
            video1_path=os.path.join(DOWNLOAD_FOLDER, data['video1']),
            video2_path=os.path.join(DOWNLOAD_FOLDER, data['video2']),
            output_path=output_path,
            pos1=pos1,
            size1=size1,
            pos2=pos2,
            size2=size2,
            video_scale=data.get('video_scale', 1.0)
        )

        # 验证输出文件是否创建成功
        if not os.path.exists(output_path):
            error_msg = f"视频生成失败: {output_path}"
            app.logger.error(error_msg)
            return jsonify({
                "error": error_msg,
                "output_path": output_path
            }), 500

        # 获取文件大小
        file_size = os.path.getsize(output_path)

        # 返回成功响应
        download_url = f"/download_composite/{output_filename}"
        app.logger.info(f"视频合成成功: {download_url}")
        return jsonify({
            "success": True,
            "filename": output_filename,
            "file_size": file_size,
            "download_url": download_url
        })

    except TimeoutError as te:
        app.logger.error(f"视频合成超时: {str(te)}")
        return jsonify({
            "error": "视频处理超时",
            "message": "视频处理时间过长，请尝试使用更短的视频或降低分辨率",
            "exception_type": "TimeoutError"
        }), 500
    except Exception as e:
        app.logger.error(f"视频合成失败: {str(e)}", exc_info=True)
        return jsonify({
            "error": f"处理失败: {str(e)}",
            "exception_type": type(e).__name__
        }), 500


# 清理旧文件
@app.before_request
def clean_old_files():
    """定期清理超过24小时的临时文件"""
    now = datetime.now()
    for filename in os.listdir(DOWNLOAD_FOLDER):
        file_path = os.path.join(DOWNLOAD_FOLDER, filename)
        if os.path.isfile(file_path):
            file_time = datetime.fromtimestamp(os.path.getctime(file_path))
            if now - file_time > timedelta(hours=24):
                try:
                    os.remove(file_path)
                    app.logger.info(f"已清理旧文件: {filename}")
                except Exception as e:
                    app.logger.error(f"清理文件失败: {file_path} - {str(e)}")


# 列出可用媒体文件
@app.route('/api/list_available_media', methods=['GET'])
def list_available_media():
    """列出可用媒体文件"""
    try:
        ppt_dir = os.path.join(app.static_folder, 'ppt')
        video_dir = DOWNLOAD_FOLDER

        ppt_files = [
            f for f in os.listdir(ppt_dir)
            if f.endswith(('.png', '.jpg', '.jpeg'))
        ]

        video_files = [
            f for f in os.listdir(video_dir)
            if f.endswith(('.mp4', '.avi', '.mov'))
        ]

        return jsonify({
            "ppt_templates": ppt_files,
            "videos": video_files
        })

    except Exception as e:
        return jsonify({
            "error": f"无法列出媒体文件: {str(e)}"
        }), 500


# 检查FFmpeg是否可用
@app.route('/api/check_ffmpeg', methods=['GET'])
def check_ffmpeg():
    """检查FFmpeg是否可用"""
    try:
        result = subprocess.run(
            ['ffmpeg', '-version'],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True
        )

        if result.returncode == 0:
            return jsonify({
                "available": True,
                "version": result.stdout.split('\n')[0]
            })
        else:
            return jsonify({
                "available": False,
                "error": result.stderr
            })
    except Exception as e:
        return jsonify({
            "available": False,
            "error": str(e)
        })


if __name__ == "__main__":
    app.run(debug=True, port=5000)